1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.audio;
12 
13 public import hip.audio.audiosource;
14 public import hip.api.audio : HipAudioType, DistanceModel, HipAudioImplementation, getAudioImplementationForOS, IHipAudioPlayer, setIHipAudioPlayer;
15 import hip.config.audio;
16 
17 //Backends
18 import hip.audio_decoding.audio;
19 import hip.math.utils:getClosestMultiple;
20 import hip.error.handler;
21 
22 class HipAudioImpl : IHipAudioPlayer
23 {
24     public bool initialize(HipAudioImplementation implementation = HipAudioImplementation.OpenAL,
25     bool hasProAudio = false,
26     bool hasLowLatencyAudio = false,
27     int  optimalBufferSize = 4096,
28     int optimalSampleRate = 44_100)
29     {
30         import hip.console.log;
31         ErrorHandler.startListeningForErrors("HipremeAudio initialization");
32         _hasInitializedAudio = true;
33         this.is3D = is3D;
34         audioInterface = getAudioInterface(implementation);
35         this.hasProAudio        = hasProAudio;
36         this.hasLowLatencyAudio = hasLowLatencyAudio;
37         this.optimalBufferSize  = optimalBufferSize;
38         this.optimalSampleRate  = optimalSampleRate;
39 
40         loglnInfo("Hipreme Audio Started: ", implementation,
41             "\nPro Audio: ", hasProAudio,
42             "\nLow Latency: ", hasLowLatencyAudio,
43             "\nOptimal Buffer Size: ", optimalBufferSize,
44             "\nOptimal Sample Rate: ", optimalSampleRate
45         );
46         return ErrorHandler.stopListeningForErrors();
47     }
48     bool pause(AHipAudioSource src)
49     {
50         src.isPlaying = false;
51         return false;
52     }
53     bool play_streamed(AHipAudioSource src)
54     {
55         audioInterface.play_streamed(src);
56         src.isPlaying = true;
57         return false;
58     }
59     IHipAudioClip getClip(){return audioInterface.getClip();}
60 
61     /**
62     *   Loads a file from disk, sets the chunkSize for streaming and does one decoding frame
63     */
64     IHipAudioClip loadStreamed(string path, uint chunkSize = ushort.max+1)
65     {
66         chunkSize = getClosestMultiple(optimalBufferSize, chunkSize);
67         IHipAudioClip buf = audioInterface.loadStreamed(path, chunkSize);
68         return buf;
69     }
70 
71     void updateStream(AHipAudioSource source)
72     {
73         audioInterface.updateStream(source);
74     }
75     AHipAudioSource getSource(bool isStreamed = false, IHipAudioClip clip = null)
76     {
77         if(isStreamed) ErrorHandler.assertExit(clip !is null, "Can't get streamed source without any buffer");
78         HipAudioSource ret = cast(HipAudioSource)audioInterface.getSource(isStreamed);
79         if(clip)
80             ret.clip = clip;
81         return ret;
82     }
83     void onDestroy()
84     {
85         if(audioInterface !is null)
86             audioInterface.onDestroy();
87         audioInterface = null;
88     }
89 
90     void update()
91     {
92         if(audioInterface !is null)
93             audioInterface.update();
94     }
95 
96     private static IHipAudioPlayer getAudioInterface(HipAudioImplementation impl,
97     bool hasProAudio = false,
98     bool hasLowLatencyAudio = false,
99     int  optimalBufferSize = 4096,
100     int optimalSampleRate = 44_100)
101     {
102         import hip.console.log;
103         final switch(impl)
104         {
105             case HipAudioImplementation.WebAudio:
106             {
107                 static if(HasWebAudio)
108                 {
109                     import hip.audio.backend.webaudio.player;
110                     return new HipWebAudioPlayer(AudioConfig.musicConfig);
111                 }
112                 else
113                 {
114                     loglnWarn("Tried to use WebAudio implementation, but not in WebAssembly. No audio available");
115                     goto case HipAudioImplementation.Null;
116                 }
117             }
118             case HipAudioImplementation.OpenSLES:
119                 static if(HasOpenSLES)
120                 {
121                     import hip.audio.backend.opensles.player;
122                     return new HipOpenSLESAudioPlayer(AudioConfig.androidConfig,
123                     hasProAudio,
124                     hasLowLatencyAudio,
125                     optimalBufferSize,
126                     optimalSampleRate);
127                     break;
128                 }
129             case HipAudioImplementation.XAudio2:
130                 static if(HasXAudio2)
131                 {
132                     import hip.audio.backend.xaudio.player;
133                     loglnInfo("Initializing XAudio2 with audio config ", AudioConfig.musicConfig);
134                     return new HipXAudioPlayer(AudioConfig.musicConfig);
135                 }
136                 else 
137                 {
138                     loglnWarn("Tried to use XAudio2 implementation, but no XAudio2 version was provided. OpenAL will be used instead");
139                     goto case HipAudioImplementation.OpenAL;
140                 }
141             case HipAudioImplementation.AVAudioEngine:
142             {
143                 static if(HasAVAudioEngine)
144                 {
145                     import hip.audio.backend.avaudio.player;
146                     return new HipAVAudioPlayer(AudioConfig.androidConfig);
147                 }
148                 else
149                 {
150                     loglnWarn("Tried to use AVAudioEngine implementation, but no AVAudioEngine found. OpenAL will be used instead");
151                     goto case HipAudioImplementation.OpenAL;
152                 }
153             }
154             case HipAudioImplementation.OpenAL:
155             {
156                 static if(HasOpenAL)
157                 {
158                     import hip.audio.backend.openal.player;
159                     //Please note that OpenAL HRTF(spatial sound) only works with Mono Channel
160                     return new HipOpenALAudioPlayer(AudioConfig.musicConfig);
161                 }
162                 else
163                 {
164                     loglnWarn("Tried to use OpenAL implementation, but no OpenAL version was provided. No audio available.");
165                     goto case HipAudioImplementation.Null;
166                 }
167             }
168             case HipAudioImplementation.Null:
169             {
170                 import hip.audio.backend.nullaudio;
171                 loglnWarn("No AudioInterface was found. Using NullAudio");
172                 return new HipNullAudio();
173             }
174         }
175     }
176 
177 
178    
179     protected bool hasProAudio;
180     protected bool hasLowLatencyAudio;
181     protected int  optimalBufferSize;
182     protected int  optimalSampleRate;
183     private   bool is3D;
184     private   uint activeSources;
185 
186     IHipAudioPlayer audioInterface;
187 
188     //Debug vars
189     private bool _hasInitializedAudio = false;
190     public bool hasInitializedAudio() => _hasInitializedAudio;
191 }
192 
193 
194 private __gshared HipAudioImpl player;
195 void PreInitializeHipAudio()
196 {
197     player = new HipAudioImpl();
198     setIHipAudioPlayer(player);
199 }
200 
201 pragma(inline, true)
202 HipAudioImpl HipAudio(){return player;}
203 
204 export extern(C) IHipAudioPlayer HipAudioPlayerAPI()
205 {
206     return player;
207 }